/* 
 * File:   Sphere.cpp
 * Author: brady
 * 
 * Created on June 24, 2011, 4:20 PM
 */

#include <shapes/Sphere.h>

Sphere::Sphere() {
    origin = Vector3D(0.0,0.0,0.0);
    radius = 0;
}

Sphere::Sphere(const Vector3D& origin, double radius){
    this->origin = origin;
    this->radius = radius;
    buildBoundingBox();
}

Sphere::Sphere(const Sphere& orig) {
    this->origin = orig.origin;
    this->radius = orig.radius;
    this->boundingBox = orig.boundingBox;
}

Sphere::~Sphere() {
}

bool Sphere::testIntersect(IntersectionParams& ip, IntersectionResult& ir)
{
    ir.shape = NULL;

    double tmin = ip.getTMin();
    double tmax = ip.getTMax();
    double discriminant;
    const Vector3D& d = ip.getRay().getDirection();
    const Vector3D& e = ip.getRay().getOrigin();
    const Vector3D& o = origin;

    double c = (e-o).dot(e-o) - radius*radius;
    double b = 2 * d.dot(e-o);
    double a = d.dot(d);

    discriminant = (b*b) - (4.0*a*c);
    if(discriminant < 0)
    {
        return false;
    }

    double t0, t1, smaller, larger;
    t0 = (-b + sqrt(discriminant)) / (2.0*a);
    t1 = (-b - sqrt(discriminant)) / (2.0*a);
    if(t0 < t1)
    {
        smaller = t0;
        larger = t1;
    }
    else
    {
        smaller = t1;
        larger = t0;
    }

    if(smaller > tmin && smaller < tmax){
        ir.tVal = smaller;
        ir.shape = this;
        ir.ray = Ray(ip.getRay());
        ir.hitPoint = ir.ray.follow(ir.tVal);
        ir.normal = ir.hitPoint - origin;
        ir.normal.normalize();
        ir.textureCoords = Vector3D(0,0,0);
        return true;
    }
    if(larger > tmin && larger < tmax)
    {

        ir.tVal = larger;
        ir.shape = this;
        ir.ray = Ray(ip.getRay());
        ir.hitPoint = ir.ray.follow(ir.tVal);
        ir.normal = ir.hitPoint - origin;
        ir.normal.normalize();
        ir.textureCoords = Vector3D(0,0,0);
        return true;
    }
    return false;
}

void Sphere::buildBoundingBox(){
    Vector3D max(origin[0]+radius, origin[1]+radius, origin[2]+radius);
    Vector3D min(origin[0]-radius, origin[1]-radius, origin[2]-radius);
    this->boundingBox = BoundingBox(min, max);
    boundingBox.pad(FLT_EPSILON);
}
